#ifndef STDAFX_H
#define STDAFX_H

#include <QDebug>
#include <functional>

template <typename T>
using QFunction = std::function<T>;

// singleton
template <typename T>
class NeoSingleton
{
public:
    static T *getInstance();

private:
    Q_DISABLE_COPY(NeoSingleton)
};

template <typename T>
T *NeoSingleton<T>::getInstance()
{
    static T instance;
    return &instance;
}

#define NEO_SINGLETON_CREATE(CLASS)                \
private:                                           \
    friend class NeoSingleton<CLASS>;              \
                                                   \
public:                                            \
    static CLASS *getInstance()                    \
    {                                              \
        return NeoSingleton<CLASS>::getInstance(); \
    }

// public class define
#define NEO_PUBLIC_CREATE(CLASS)                                 \
protected:                                                       \
    explicit CLASS(CLASS##Private &dd, CLASS *parent = nullptr); \
    QScopedPointer<CLASS##Private> d_ptr;                        \
                                                                 \
private:                                                         \
    Q_DISABLE_COPY(CLASS)                                        \
    Q_DECLARE_PRIVATE(CLASS)

#define NEO_PUBLIC_POINT(CLASS) CLASS##Private *const d = d_func();

#define NEO_PUBLIC_PROPERTY_CREATE_H(TYPE, NAME)            \
    Q_PROPERTY(TYPE p##NAME READ get##NAME WRITE set##NAME) \
public:                                                     \
    void set##NAME(TYPE NAME);                              \
    TYPE get##NAME() const;

#define NEO_PUBLIC_PROPERTY_CREATE_CPP(CLASS, TYPE, NAME) \
    void CLASS::set##NAME(TYPE NAME)                      \
    {                                                     \
        NEO_PUBLIC_POINT(CLASS);                          \
        if (d->p##NAME != NAME)                           \
        {                                                 \
            d->p##NAME = std::move(NAME);                 \
        }                                                 \
    }                                                     \
    TYPE CLASS::get##NAME() const                         \
    {                                                     \
        NEO_PUBLIC_POINT(const CLASS);                    \
        return d->p##NAME;                                \
    }

#define NEO_PUBLIC_PROPERTY_CREATE_NOTIFY_H(TYPE, NAME)                               \
    Q_PROPERTY(TYPE p##NAME READ get##NAME WRITE set##NAME NOTIFY neo##NAME##Changed) \
public:                                                                               \
    Q_SIGNAL void neo##NAME##Changed();                                               \
    void set##NAME(TYPE NAME);                                                        \
    TYPE get##NAME() const;

#define NEO_PUBLIC_PROPERTY_CREATE_NOTIFY_CPP(CLASS, TYPE, NAME) \
    void CLASS::set##NAME(TYPE NAME)                             \
    {                                                            \
        NEO_PUBLIC_POINT(CLASS);                                 \
        if (d->p##NAME != NAME)                                  \
        {                                                        \
            d->p##NAME = std::move(NAME);                        \
            Q_EMIT neo##NAME##Changed();                         \
        }                                                        \
    }                                                            \
    TYPE CLASS::get##NAME() const                                \
    {                                                            \
        NEO_PUBLIC_POINT(const CLASS);                           \
        return d->p##NAME;                                       \
    }

// private class define
#define NEO_PRIVATE_CREATE(CLASS) \
protected:                        \
    CLASS *q_ptr;                 \
                                  \
private:                          \
    Q_DECLARE_PUBLIC(CLASS)

#define NEO_PRIVATE_POINT(CLASS) CLASS *const q = q_func();

#define NEO_PRIVATE_PROPERTY_CREATE_H(TYPE, NAME) \
private:                                          \
    TYPE p##NAME;

// public and private class define
#define NEO_PROPERTY_CREATE(TYPE, NAME)                                             \
    Q_PROPERTY(TYPE p##NAME READ get##NAME WRITE set##NAME NOTIFY p##NAME##Changed) \
public:                                                                             \
    Q_SIGNAL void p##NAME##Changed();                                               \
    void set##NAME(TYPE NAME)                                                       \
    {                                                                               \
        if (p##NAME != NAME)                                                        \
        {                                                                           \
            p##NAME = std::move(NAME);                                              \
            Q_EMIT p##NAME##Changed();                                              \
        }                                                                           \
    }                                                                               \
    inline TYPE get##NAME() const noexcept                                          \
    {                                                                               \
        return p##NAME;                                                             \
    }                                                                               \
                                                                                    \
private:                                                                            \
    TYPE p##NAME;

// painter defines
#define NEO_PAINTER_RESET                                    \
    QPainter painter(this);                                  \
    painter.setPen(Qt::NoPen);                               \
    painter.setBrush(Qt::NoBrush);                           \
    painter.setRenderHint(QPainter::Antialiasing, true);     \
    painter.setRenderHint(QPainter::TextAntialiasing, true); \
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
#define NEO_RADIUS_ADJUSTED_VALUE 1

#define NEO_RADIUS_ADJUSTED_VALUES                        \
    NEO_RADIUS_ADJUSTED_VALUE, NEO_RADIUS_ADJUSTED_VALUE, \
        -NEO_RADIUS_ADJUSTED_VALUE, -NEO_RADIUS_ADJUSTED_VALUE

#define NEO_RADIUS_ADJUSTED_VALUES_FACTOR(FACTOR)                         \
    NEO_RADIUS_ADJUSTED_VALUE *FACTOR, NEO_RADIUS_ADJUSTED_VALUE *FACTOR, \
        -NEO_RADIUS_ADJUSTED_VALUE *FACTOR, -NEO_RADIUS_ADJUSTED_VALUE *FACTOR

#define NEO_BEGIN_ENUM_CREATE(CLASS) \
    namespace CLASS                  \
    {

#define NEO_END_ENUM_CREATE(CLASS) }

#endif // STDAFX_H